﻿using System;
using System.IO;
using System.Reflection;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;

namespace SmartCard.Framework
{
	public class DefaultFrameworkCardReaderManager : ISmartCardReaderManager
	{
		#region fields

		/// <summary>
		/// reader discovery modules
		/// </summary>
		[ImportMany(AllowRecomposition = false)]
		private IEnumerable<ISmartCardReaderDiscovery> _readerDiscoveryModules = null;

		/// <summary>
		/// list of smart card readers in the system
		/// </summary>
		private List<ISmartCardReader> _managedReaders;

		#endregion

		#region properties

		public IEnumerable<ISmartCardReader> ManagedSmartCardReaders
		{
			get
			{
				return _managedReaders;
			}
		}

		#endregion

		#region construction

		public DefaultFrameworkCardReaderManager(DirectoryInfo directory)
		{
			if (directory == null)
			{ throw new ArgumentNullException("directory"); }

			Compose(directory);
			_managedReaders = FindReadersToManage();
			AttachToReaderNotifications();
		}

		public DefaultFrameworkCardReaderManager()
			: this(new FileInfo(
				new Uri(Assembly.GetExecutingAssembly().GetName().CodeBase).LocalPath).Directory)
		{ }

		#endregion

		#region events

		public event EventHandler<CardInsertedEventArgs> CardInserted = delegate { };
		public event EventHandler<CardRemovedEventArgs> CardRemoved = delegate { };
		public event EventHandler<ReaderErrorEventArgs> ReaderError = delegate { };

		#endregion

		#region protected

		protected virtual void SendCardInsertedEvent(CardInsertedEventArgs eventArgs)
		{
			CardInserted(this, eventArgs);
		}

		protected virtual void SendCardRemovedEvent(CardRemovedEventArgs eventArgs)
		{
			CardRemoved(this, eventArgs);
		}

		protected virtual void SendReaderErrorsEvent(ReaderErrorEventArgs eventArgs)
		{
			ReaderError(this, eventArgs);
		}

		#endregion

		#region privates

		private void Compose(DirectoryInfo directory)
		{
			using (AggregateCatalog catalog = new AggregateCatalog())
			using (var container = new CompositionContainer(catalog))
			{
				catalog.Catalogs.Add(new DirectoryCatalog(directory.FullName));
				container.ComposeParts(this);
			}
		}

		private List<ISmartCardReader> FindReadersToManage()
		{
			var readersToManage = new List<ISmartCardReader>();

			foreach (var discoveryProcess in _readerDiscoveryModules)
			{
				var discoveredReaders = discoveryProcess.Discover();
				readersToManage.AddRange(discoveredReaders);
			}

			return readersToManage;
		}

		private void AttachToReaderNotifications()
		{
			foreach (var managedReader in _managedReaders)
			{
				managedReader.CardInserted += managedReader_CardInserted;
				managedReader.CardRemoved += managedReader_CardRemoved;
				managedReader.ReaderError += managedReader_ReaderError;
			}
		}

		private void managedReader_ReaderError(object sender, ReaderErrorEventArgs eventArgs)
		{
			SendReaderErrorsEvent(eventArgs);
		}

		private void managedReader_CardRemoved(object sender, CardRemovedEventArgs eventArgs)
		{
			SendCardRemovedEvent(eventArgs);
		}

		private void managedReader_CardInserted(object sender, CardInsertedEventArgs eventArgs)
		{
			SendCardInsertedEvent(eventArgs);
		}

		#endregion
	}
}
